package utilities;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

/**
 * 
 * @author fparks
 * 
 */
public final class AssertionAnalyser {
	private static final Logger LOG = LoggerFactory
			.getLogger(AssertionAnalyser.class);
	private static final String XPATH_GET_ALL_ASSERTIONS = "//ResponseAssertion";
	private static final String XPATH_GET_ALL_PATTERNS_IN_ASSERTION = "./collectionProp/stringProp";
	private static final String XPATH_GET_COLLECTION_PROP_ELEMENT = "./collectionProp";
	private static final String XPATH_GET_PARENT_ELEMENT = "./..";
	
	//private no args constructor
	private AssertionAnalyser(){}
	
	/**
	 * parses the passed JMX document for assertions that have multiple
	 * patterns.
	 * @param doc xml document
	 * @return empty list if no such elements are found
	 */
	public static List<Element> findMultipleAssertionPatterns(Document doc){
		XPath path = XPathFactory.newInstance().newXPath();
		NodeList assertions = null;
		NodeList patterns;
		List<Element> multiplePatters = new ArrayList<Element>();
		Element assertion;
		
		try {
			//grab all assertions from the document
			assertions = (NodeList) path.evaluate(XPATH_GET_ALL_ASSERTIONS, doc, XPathConstants.NODESET);
			//for each assertion grab all of the patterns
			for(int index = 0; index < assertions.getLength(); index++){
				assertion = (Element) assertions.item(index);
				patterns = (NodeList) path.evaluate(XPATH_GET_ALL_PATTERNS_IN_ASSERTION, assertion, XPathConstants.NODESET);
				//if there is more than one pattern put the assertion to be returned
				if(1 < patterns.getLength()){
					multiplePatters.add(assertion);
				}
			}
		} catch (XPathExpressionException e) {
			LOG.error("**XPathExpressionException - multiple assertions not found**");
		}
		return multiplePatters;
	}
	
	/**
	 * 
	 * @param originalAssertion that contains multiple assertions
	 * @param doc host xml document
	 * @throws XPathExpressionException 
	 */
	public static void breakMultipelPatternsIntoAssertions(Element originalAssertion, Document doc) throws XPathExpressionException{
		XPath path = XPathFactory.newInstance().newXPath();
		Element hashTree = (Element) path.evaluate(XPATH_GET_PARENT_ELEMENT, originalAssertion, XPathConstants.NODE);
		Element collectionProp;
		Element duplicateAssertion;
		NodeList patterns = (NodeList) path.evaluate(XPATH_GET_ALL_PATTERNS_IN_ASSERTION, originalAssertion, XPathConstants.NODESET);
		Element pattern;
		Element newHashTree;
		//remove all patters except the first from the original assertion
		collectionProp = (Element) path.evaluate(XPATH_GET_COLLECTION_PROP_ELEMENT, originalAssertion, XPathConstants.NODE);
		for(int index = 1; index < patterns.getLength(); index++){
			collectionProp.removeChild(patterns.item(index));
		}
		//create a new assertion for each pattern after the first
		for(int index = 1; index < patterns.getLength(); index++){
			pattern = (Element) patterns.item(index);
			//make a copy of the original assertion
			duplicateAssertion = (Element) originalAssertion.cloneNode(true);
			newHashTree = doc.createElement("hashTree");
			//change the name of the copy
			duplicateAssertion.setAttribute("testname", createName(pattern));
			//replace the pattern in the duplicate assertion with the current pattern
			collectionProp = (Element) path.evaluate(XPATH_GET_COLLECTION_PROP_ELEMENT, duplicateAssertion, XPathConstants.NODE);
			Element oldPattern = (Element) path.evaluate(XPATH_GET_ALL_PATTERNS_IN_ASSERTION, duplicateAssertion, XPathConstants.NODE);
			collectionProp.replaceChild(pattern, oldPattern);
			//add to copy into the document
			hashTree.appendChild(duplicateAssertion);
			hashTree.appendChild(newHashTree);
			
		}
	}

	/**
	 * create a meaningful name based on the pattern
	 * (hopefully unique)
	 * first word of the pattern + the numeric name of the element
	 * @param attribute
	 * @return
	 */
	private static String createName(Element pattern) {
		String patternValue = pattern.getTextContent();
		String patternName = pattern.getAttribute("name");
		StringBuilder newName = new StringBuilder();
		
		//extract the first word from the pattern value
		Pattern firstWordPattern = Pattern.compile("^([\\w\\-]+)");
		Matcher matches = firstWordPattern.matcher(patternValue);
		if( matches.find() ){
			newName.append( matches.group(1) );
		}
		//add the numeric name after the first word
		newName.append(patternName);
		
		return newName.toString();
	}
}
